﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Terminal.Advanced_AEUC_settings_controls.Settings_AUX;

namespace Terminal.Advanced_AEUC_settings_controls.Scripting
{
    /// <summary>
    /// Send message script action
    /// </summary>
    class ScrActStrctMarshalling : ScrBasicAction
    {
        /// <summary>
        /// Message sending callback
        /// </summary>
        CommLink.CommLink.MessageSend sendFunc;
        /// <summary>
        /// Semaphore for message reception synchronization
        /// </summary>
        AutoResetEvent waitMsg;
        /// <summary>
        /// Byte array representing the message received.
        /// </summary>
        byte[] msgBytestream;
        /// <summary>
        /// Reference to the messaging script module
        /// </summary>
        ScrMsgCntr msgCntr;

        /// <summary>
        /// Public constructor for the script action
        /// </summary>
        /// <param name="iDispName">Action display text</param>
        /// <param name="iUsageHelp">Usage help and comments text for the action</param>
        /// <param name="callback">Callback function to be used for sending messages</param>
        public ScrActStrctMarshalling(string iDispName, string iUsageHelp, CommLink.CommLink.MessageSend callback, ScrMsgCntr iMsgCntr)
            : base(iDispName, iUsageHelp)
        {
            sendFunc = callback;
            waitMsg = new AutoResetEvent(false);
            
            msgCntr = iMsgCntr;
            msgCntr.DispatchEnabled = false;
            msgCntr.OnMessageReceived += ReceiveMsg;

            usageHelp += "Input params: Marshalling - mem alloc GenMSG(message name) + Marshalling - mem copy GenMSG(message name) +\n Varriable argument list of GenPayloadStruct(payload struct name)\n";
        }

        /// <summary>
        /// Inform the wait for message action that a message has been received and provide the message data.
        /// </summary>
        /// <param name="byteStream">Byte array of the message data</param>
        public void ReceiveMsg(byte[] byteStream)
        {
            msgBytestream = byteStream;
            waitMsg.Set();
        }

        /// <summary>
        /// Perform the action using specified parameters.
        /// </summary>
        /// <param name="paramList">List of the input/output parameters for the action</param>
        /// <returns>True if action is succesful</returns>
        public override bool Do(List<object> paramList)
        {
            if (breakPoint)
                WaitUserInput();

            if (paramList.Count > 3 && paramList[0].GetType() == typeof(GenMSG) && 
                paramList[1].GetType() == typeof(GenMSG) && 
                paramList[2].GetType() == typeof(GenPayloadStruct))
            {
                GenMSG msgMarshalAlloc = (GenMSG)paramList[0];
                GenMSG msgHdr = new GenMSG();
                msgHdr.msgId = new GenMSGId();
                msgHdr.msgStatus = new GenMSGStatus();
                GenMSG msgMarshalCopy = (GenMSG)paramList[1];

                List<GenPayloadStruct> mrshPayloadStructs = new List<GenPayloadStruct>();
                List<GenPayloadStruct.srzOfstMap> mrshSrzMap = new List<GenPayloadStruct.srzOfstMap>();
                #region Add the payloads of all of the OMX structures bearing the same message id/stat
                for (int i = 2; i < paramList.Count; i++)
                {
                    mrshPayloadStructs.Add((GenPayloadStruct)paramList[i]);
                    GenPayloadStruct.srzOfstMap tmpItm = new GenPayloadStruct.srzOfstMap();
                    tmpItm.id = (uint)((GenPayloadStruct)paramList[i])[((GenPayloadStruct)paramList[i])["structId"]];
                    tmpItm.ofst = 0;
                    mrshSrzMap.Add(tmpItm);
                }
                #endregion
                #region Create the serialization structure-address mapping
                uint strmUsg = 0;
                for (int i = 0; i < mrshPayloadStructs.Count; i++)
                {
                    mrshSrzMap[i].ofst = strmUsg;
                    strmUsg += (uint)mrshPayloadStructs[i].prepareByteStream().Length;
                }
                #endregion
                #region Prepare marhsalling memory allocation message
                GenPayloadStruct.NewPldItem[] runTVar = new GenPayloadStruct.NewPldItem[3];
                int ittr = 0;

                runTVar[ittr] = new GenPayloadStruct.NewPldItem();
                runTVar[ittr].AEUC_DisplayName = "ComponentId";
                runTVar[ittr].AEUC_Val = (uint)((GenPayloadStruct)paramList[2])[((GenPayloadStruct)paramList[2])["compId"]];
                runTVar[ittr].AEUC_Id = (uint)ittr++;

                runTVar[ittr] = new GenPayloadStruct.NewPldItem();
                runTVar[ittr].AEUC_DisplayName = "Size";
                runTVar[ittr].AEUC_Val = strmUsg;
                runTVar[ittr].AEUC_Id = (uint)ittr++;

                runTVar[ittr] = new GenPayloadStruct.NewPldItem();
                runTVar[ittr].AEUC_DisplayName = "Pointer";
                runTVar[ittr].AEUC_Val = 0;
                runTVar[ittr].AEUC_Id = (uint)ittr++;
                #endregion
                
                msgMarshalAlloc.payloadStruct = new GenPayloadStruct("marshalling memory allocation", runTVar);
                msgHdr.payloadStruct = msgMarshalAlloc.payloadStruct;

                sendFunc(msgMarshalAlloc.GenerateMessage(null));

                do
                {
                    msgCntr.DispatchEnabled = true;
                    waitMsg.WaitOne(-1);
                    if (msgBytestream != null)
                    {
                        msgHdr.msgBytestream = msgBytestream;
                        msgHdr.ParseMessageDataHeaderOnly();

                        if (msgHdr.msgId.UID == msgMarshalAlloc.msgId.UID)
                        {
                            msgHdr.ParseMessageData();

                            uint mhsPtr = (uint)msgMarshalAlloc.payloadStruct[2];
                            #region Apply the base pointer to all offsets
                            for (int i = 0; i < mrshSrzMap.Count; i++)
                            {
                                mrshSrzMap[i].ofst += mhsPtr;
                            }
                            #endregion
                            byte[] msgMarshallingByteStream = null;
                            #region Create the all-in-one bytestream containing all the OMX structure payloads
                            msgMarshallingByteStream = msgMarshalAlloc.payloadStruct.prepareByteStream();
                            for (int i = 0; i < mrshPayloadStructs.Count; i++)
                            {
                                byte[] nwStream = mrshPayloadStructs[i].prepareByteStream(mrshSrzMap.ToArray());
                                if (msgMarshallingByteStream == null)
                                    msgMarshallingByteStream = nwStream;
                                else
                                {
                                    byte[] tpStream = new byte[msgMarshallingByteStream.Length + nwStream.Length];
                                    msgMarshallingByteStream.CopyTo(tpStream, 0);
                                    nwStream.CopyTo(tpStream, msgMarshallingByteStream.Length);
                                    msgMarshallingByteStream = tpStream;
                                }
                            }
                            #endregion

                            sendFunc(msgMarshalCopy.GenerateMessage(msgMarshallingByteStream));
                            return true;
                        }
                    }
                } while (true);
            }

            return false;
        }

        /// <summary>
        /// Returns a List containing objects of the parameter list required for the Do function.
        /// </summary>
        /// <returns>List of parameter objects</returns>
        public override List<Type> GetParamList()
        {
            List<Type> paramList = new List<Type>();
            paramList.Add(typeof(GenMSG));
            paramList.Add(typeof(GenMSG));
            paramList.Add(typeof(ScrVarArgListType));
            paramList.Add(typeof(GenPayloadStruct));

            return paramList;
        }
    }
}

